Заметки докладчика:
- Это итоговая теоретическая лекция курса — объединяет все предыдущие темы: сети, ООП, полиморфизм, исключения, потоки, GUI
- Лабораторная работа: удалённый калькулятор на основе RPC (JSON-RPC или XML-RPC)
- Лекция очень длинная — возможно потребуется 2 занятия
- Рекомендуемое распределение времени:
1. Понятие распределённых систем и архитектуры — 15 мин
2. RPC, XML-RPC, JSON-RPC с примерами кода — 25 мин
3. Удалённые объекты (ООП + RPC) — 15 мин
4. Web Services (SOAP vs REST) — 15 мин
5. Безопасность (JWT, SSL/TLS) — 15 мин
6. Развертывание, мониторинг, балансировка нагрузки — 15 мин
- Если время ограничено: SOAP и мониторинг можно сократить, балансировку — упомянуть обзорно
Заметки докладчика:
- Ключевые свойства: автономность (узлы независимы), гетерогенность (разные платформы/ОС/языки), масштабируемость
- Теорема CAP: невозможно одновременно обеспечить все три: Consistency (согласованность), Availability (доступность), Partition tolerance (устойчивость к разделению)
- Пример для обсуждения: чат-приложение — сервер хранит историю сообщений, несколько клиентов подключаются
- Если сервер падает — клиенты теряют связь (partition tolerance)
- Если два сервера не синхронизированы — разные клиенты видят разную историю (consistency)
- Архитектурные стили: клиент-сервер (централизованный), P2P (децентрализованный), многоуровневая (3-tier)
- Обратить внимание на то, что распределённая система выглядит как единая система для пользователя
Заметки докладчика:
- Клиент-сервер проще контролировать и защищать: есть центральная точка авторизации, логирования и обновления.
- Минус клиент-серверной модели — центральный сервер становится потенциальной точкой отказа и узким местом производительности.
- P2P повышает живучесть, но усложняет согласованность данных, поиск узлов, маршрутизацию и безопасность.
- Многоуровневая архитектура часто используется в бизнес-приложениях: UI-клиент обращается к API, API обращается к БД и внешним сервисам.
- Связать с RPC: RPC обычно применяют между уровнями системы, когда один компонент должен вызвать операцию другого компонента.
Заметки докладчика:
- RPC — это не один конкретный протокол, а подход к построению взаимодействия между программами.
- Главное обещание RPC: скрыть детали сети за привычной моделью вызова функции.
- Важно сразу предупредить: полная прозрачность невозможна, потому что сеть медленнее памяти и может отказывать.
- Пример из жизни: клиент вызывает add(5, 3), но фактически происходит сериализация, отправка по сети, обработка на сервере и получение ответа.
- Подчеркнуть различие: локальный вызов обычно надежен и быстрый, удаленный вызов всегда связан с задержкой, таймаутом и возможной недоступностью сервера.
Заметки докладчика:
- RPC особенно полезен там, где клиенту нужна не просто передача данных, а выполнение конкретной операции: calculate(), getUser(), createOrder().
- В корпоративных системах RPC часто используется между внутренними сервисами, потому что там важен строгий контракт и предсказуемая схема вызовов.
- Для студентов: удаленный калькулятор — учебная миниатюра реальной схемы, где клиент не считает сам, а делегирует операцию серверу.
- Можно сравнить с библиотекой: локальная библиотека подключается в процесс, а RPC-сервис расположен в другом процессе или на другом компьютере.
Заметки докладчика:
- Нарисовать на доске цепочку: client -> client stub -> transport -> server stub -> real method.
- Stub можно объяснить как адаптер между обычным вызовом функции и сетевым сообщением.
- Dispatcher на сервере похож на таблицу маршрутов: по строке "calculator.add" выбирается нужная функция.
- Маршаллинг нужен из-за того, что указатели, ссылки и объекты памяти нельзя напрямую передать в другой процесс.
- В Qt роль транспорта могут выполнять QTcpSocket, QNetworkAccessManager, WebSocket или другой сетевой механизм.
Заметки докладчика:
- Контракт — центральная идея RPC: клиент должен знать, какие методы существуют и какие параметры допустимы.
- В gRPC контракт описывается в .proto-файлах, в SOAP — через WSDL, в Thrift — через IDL.
- JSON-RPC и XML-RPC могут работать без отдельного IDL, но контракт все равно существует хотя бы в документации.
- Обратить внимание на версионирование: изменение параметров метода может сломать старых клиентов.
- Хорошая практика: не удалять и не менять смысл существующих полей без необходимости; добавлять новые поля совместимо.
Заметки докладчика:
- Простое различие: RPC спрашивает "какую функцию вызвать?", REST спрашивает "с каким ресурсом работаем?".
- RPC удобен для команд и операций: calculateTax, generateReport, reserveTicket.
- REST удобен для CRUD-модели: получить пользователя, создать заказ, обновить профиль.
- В реальных системах подходы часто смешиваются: публичное API может быть REST, а внутренние сервисы общаются через gRPC.
- Не утверждать, что один подход всегда лучше: выбор зависит от предметной области, требований к производительности и удобства сопровождения.
Заметки докладчика:
- Это один из самых важных теоретических моментов RPC: ошибка сети не говорит, выполнилась ли операция на сервере.
- Пример: клиент отправил "списать 100 рублей", сервер списал деньги, но ответ потерялся. Повтор запроса может списать деньги второй раз.
- Идемпотентность означает, что повторный вызов не меняет результат сверх первого выполнения. Например, setStatus("paid") безопаснее, чем chargeCard(100).
- JSON-RPC поле id помогает сопоставлять ответ с запросом, но само по себе не решает проблему повторного выполнения.
- Exactly once в распределенных системах обычно требует хранения состояния, транзакций, журналов и дедупликации; на практике часто проектируют операции под at least once или at most once.
Заметки докладчика:
- Частая ошибка начинающих: считать любой сбой "ошибкой сервера". На практике нужно понимать уровень, на котором произошел сбой.
- Таймаут — это не доказательство, что операция не выполнилась.
- Для демонстрации можно спросить: какие операции безопасно повторять? getBalance — обычно да, transferMoney — нет без idempotency key.
- В JSON-RPC ошибки возвращаются в поле error, а транспортные ошибки приходят через HTTP/сокет.
- В Qt сетевые ошибки удобно получать через QNetworkReply::error() или сигналы QAbstractSocket.
Заметки докладчика:
- В Qt нельзя надолго блокировать главный поток GUI: окно перестанет отвечать.
- Синхронный стиль проще для учебного примера, но в реальных приложениях часто используют асинхронность.
- Асинхронность усложняет код: нужно хранить состояние запроса, сопоставлять ответы по id, обрабатывать отмену и таймауты.
- Можно связать с предыдущими темами курса: event loop, сигналы и слоты, многопоточность.
Заметки докладчика:
- Хорошая фраза для запоминания: "RPC выглядит как обычный вызов функции, но ведет себя как сеть".
- Не скрывать от студентов сложность: абстракция полезна, но опасна, если забыть про таймауты, повторы и частичные отказы.
- Привести пример: локальная функция add() выполняется микросекунды, а удаленная add() может занять десятки миллисекунд из-за сети.
- В производительном коде нельзя делать сотни мелких RPC-вызовов в цикле, если можно отправить один пакетный запрос.
Заметки докладчика:
- RPC скрывает сетевую сложность — клиент вызывает удалённую функцию как локальную
- Паттерн Proxy: stub на клиенте маршаллизует параметры, отправляет по сети, серверный stub демаршаллизует и вызывает реальную функцию
- XML-RPC: более старый, тяжёлый формат (вербальный XML), но строгая типизация
- JSON-RPC: современный, лёгкий, лучше читаемость, проще интеграция с JavaScript
- Ключевые понятия: marshalling (сериализация) и unmarshalling (десериализация)
- Проблемы RPC: задержки сети, потери пакетов, разные типы данных на клиенте/сервере
- В Qt: QNetworkAccessManager + сигналы/слоты для асинхронных вызовов
- CalculatorProxy — пример паттерна Proxy из лекций по ООП (виртуальные функции, наследование)
Заметки докладчика:
- XML-RPC использует HTTP как транспортный протокол (POST-запросы)
- В Qt: QNetworkAccessManager для HTTP-запросов клиента, QXmlStreamReader/QXmlStreamWriter для парсинга XML
- Серверная реализация: QTcpServer + реестр методов (QMap с именами методов и лямбдами/функторами)
- std::function<QVariant(QVariantList)> — тип для обработчиков методов
- Клиент сериализует параметры в XML, отправляет POST, парсит XML-ответ
- Типы данных XML-RPC: int, boolean, string, double, dateTime.iso8601, base64, array, struct
- Ограничения: verbose формат, медленный парсинг XML, но строгая типизация и широкая поддержка
- В продакшене: валидация входящего XML, обработка ошибок (faultCode/faultString), timeout
- Попросить студентов сравнить размер XML-RPC и JSON-RPC запросов визуально
Заметки докладчика:
- JSON-RPC 2.0 — современный стандарт, описан на jsonrpc.org/specification
- Формат запроса: {"jsonrpc":"2.0","method":"...","params":[...],"id":1}
- Поля: jsonrpc (версия), method (имя метода), params (массив или объект), id (для корреляции запроса и ответа)
- Ответ: содержит "result" при успехе или "error" при ошибке, плюс "id" для сопоставления
- Уведомления: запрос без id — сервер не присылает ответ
- Qt имеет встроенную поддержку JSON: QJsonDocument, QJsonObject, QJsonArray, QJsonValue
- Значительно проще XML-RPC — меньше кода, быстрее парсинг, легче отладка
- QJsonDocument::fromJson() и toJson() — основные методы сериализации/десериализации
- В продакшене: валидация параметров, обработка ошибок, timeout для запросов
Заметки докладчика:
- gRPC можно представить как "современный промышленный RPC": контракт, генерация кода, бинарная сериализация, HTTP/2.
- Protocol Buffers компактнее JSON и XML, потому что передают бинарное представление, а не текст с именами тегов.
- HTTP/2 дает мультиплексирование: несколько запросов могут идти по одному соединению без ожидания друг друга.
- Streaming в gRPC бывает серверный, клиентский и двунаправленный; это полезно для телеметрии, чатов и потоковой обработки.
- REST чаще удобнее для публичных веб-API, а gRPC — для внутреннего взаимодействия сервисов, где обе стороны контролируются разработчиками одной системы.
- Apache Thrift похож по идее: IDL -> генерация кода -> вызовы между языками, но исторически использует другой стек.
Заметки докладчика:
- Удаленный метод — развитие идеи RPC для объектно-ориентированного программирования.
- Вместо вызова свободной функции calculator.add() клиент вызывает метод объекта calculator->add().
- Важно различать объект и его прокси: прокси живет в клиентском процессе, настоящий объект — на сервере.
- Сложность удаленных объектов выше, потому что появляется состояние, жизненный цикл объекта, удаление, ссылки и права доступа.
- Исторические примеры: Java RMI, CORBA, DCOM. Современные системы чаще используют более простые сервисные контракты.
Заметки докладчика:
- SOAP: XML-based, строгая схема (WSDL), тяжелый, много overhead, но мощная типизация и стандарты безопасности (WS-Security)
- REST: HTTP-методы (GET/POST/PUT/DELETE), безсессионный (stateless), JSON, простой и легковесный
- Индустриальный тренд: REST доминирует в новых разработках, SOAP остаётся в enterprise/банковской сфере
- В Qt: QNetworkAccessManager — основной класс для REST-клиентов
- REST сервер в примере: маршрутизация по QRegularExpression — упрощённый роутер
- Упомянуть GraphQL как альтернативу REST (один endpoint, клиент выбирает нужные поля)
- WSDL — контракт SOAP-сервиса, описывает доступные методы и типы данных
- REST использует HTTP-коды статуса (200, 201, 400, 401, 404, 500) для передачи результата
Заметки докладчика:
- JWT (JSON Web Token) используется для аутентификации в REST API
- Структура JWT: header.payload.signature (все части в base64)
- Header описывает алгоритм (HS256), payload содержит данные (userId, exp), signature — подпись
- Важно: payload НЕ зашифрован, только подписан — не храните секреты в payload
- В продакшене используйте готовые библиотеки (не кастомную криптографию, как в примере)
- Пример с XOR — только для демонстрации концепции, не использовать в реальных системах!
- SSL/TLS через QSslSocket обязателен для production-серверов
- Обратить внимание на обработку sslErrors — в продакшене нужно проверять сертификаты, а не игнорировать
- SSL-сертификаты: самоподписанные для разработки, Let's Encrypt для продакшена
Заметки докладчика:
- Балансировка нагрузки распределяет запросы между несколькими экземплярами сервера
- Алгоритмы: round-robin (по кругу), least connections (наименьшее число соединений), hash-based (по хэшу ключа)
- Наш пример использует least-load — выбираем сервер с минимальной текущей нагрузкой
- В продакшене: nginx, HAProxy, Traefik, облачные балансировщики (AWS ALB)
- Health checks — периодическая проверка доступности серверов (в примере каждые 30 секунд)
- Это продвинутый материал — хорошая тема для курсового проекта
- ScalableService использует QThreadPool + QtConcurrent для асинхронной обработки
- Обратить внимание на QEventLoop внутри QtConcurrent::run — позволяет ждать асинхронный результат
Заметки докладчика:
- Ожидаемые ответы:
1. RPC: вызов процедур на удалённом сервере, прозрачно для клиента; REST: работа с ресурсами через HTTP-методы (GET/POST/PUT/DELETE), безсессионный, основан на представлениях.
2. JSON-RPC: компактнее, быстрее парсится, лучше читаемость, проще интеграция с JS; XML-RPC: строгая типизация, встроенная поддержка сложных типов, больше overhead.
3. Аутентификация (JWT), авторизация (RBAC/ACL), шифрование (SSL/TLS, AES), валидация входных данных, rate limiting.
4. Скрытие сложности распределённой природы от пользователя/разработчика — клиент не знает, что вызов выполняется удалённо.
5. Дублирование данных/сервисов, балансировка нагрузки, health checks, автоматический failover, репликация.
6. Round-robin, наименьшее количество соединений, хэш-балансировка, взвешенная балансировка. Пример из лекции: least-load.
7. XML-RPC, JSON-RPC, gRPC (Protocol Buffers), SOAP, CORBA (устаревший), Java RMI.
8. ACID-транзакции (в реляционных БД), eventual consistency, кворум (система распределённого консенсуса: Raft, Paxos), версионирование данных.
9. Клиент-сервер: централизованный сервер, чёткие роли; P2P: все узлы равноправны, нет единого центра, выше отказоустойчивость.
10. gRPC, Apache Thrift, RabbitMQ/Kafka (message queues), Docker/Kubernetes, микросервисы, WebSockets, REST/GraphQL.